//
// RS Game Framework
// Copyright © 2010 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_GRAPHICS_SHADEROBJECT_HPP
#define RS_GRAPHICS_SHADEROBJECT_HPP

#include "rs/graphics/GraphicsForwards.hpp"
#include "rs/graphics/Resource.hpp"
#include "rs/graphics/ShaderDescription.hpp"
#include "rs/math/Vector2.hpp"
#include "rs/math/Vector3.hpp"
#include "rs/math/Vector4.hpp"
#include "rs/math/Matrix4x4.hpp"

namespace rs
{
    namespace graphics
    {
        class Shader : public Resource
        {
            RS_DECLARE_POLYMORPHIC_REFLECTION();
            
        public:
            
            virtual ~Shader() { };
            
            virtual void Apply() = 0;
                        
            void SetConstant(Char const* name, Float const value);
            
            void SetConstant(Char const* name, int const value);
            
            void SetConstant(Char const* name, Boolean const value);
            
            void SetConstant(Char const* name, rs::math::Vector2f const& value);

            void SetConstant(Char const* name, rs::math::Vector3f const& value);
            
            void SetConstant(Char const* name, rs::math::Vector4f const& value);
            
            void SetConstant(Char const* name, rs::math::Matrix4x4f const& value);
            
            void SetConstant(Char const* name, Float const* array, UInt32 const count);

            void SetConstant(Char const* name, int const* array, UInt32 const count);
            
            void SetConstant(Char const* name, Boolean const* array, UInt32 const count);
            
            void SetConstant(Char const* name, rs::math::Vector2f const* array, UInt32 const count);

            void SetConstant(Char const* name, rs::math::Vector3f const* array, UInt32 const count);

            void SetConstant(Char const* name, rs::math::Vector4f const* array, UInt32 const count);
            
            void SetConstant(Char const* name, rs::math::Matrix4x4f const* array, UInt32 const count);
                                    
            Boolean HasGeometryShader() const;
            
            Boolean IsVertexShaderCompiled() const;
            
            Boolean IsFragmentShaderCompiled() const;
            
            Boolean IsGeometryShaderCompile() const;
            
        protected:
            
            Shader(ShaderDescription const& description);
                    
            virtual void SetRawConstant(Char const* name, UInt8 const* data, RsSize dataSize) = 0;
                    
        protected:
            
            Boolean hasGeometryShader_;
            Boolean vertexShaderCompiled_;
            Boolean fragmentShaderCompiled_;
            Boolean geometryShaderCompiled_;
        };
                
        inline Shader::Shader(ShaderDescription const& description)
        : Resource(description),
          hasGeometryShader_(description.GetGeometryShaderFile() != 0),
          vertexShaderCompiled_(true),
          fragmentShaderCompiled_(true),
          geometryShaderCompiled_(true) {
        }
        
        inline void Shader::SetConstant(Char const* name, Float const value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, int const value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, Boolean const value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Vector2f const& value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Vector3f const& value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Vector4f const& value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Matrix4x4f const& value) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(&value), sizeof(value));
        }
        
        inline void Shader::SetConstant(Char const* name, Float const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }

        inline void Shader::SetConstant(Char const* name, int const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }
        
        inline void Shader::SetConstant(Char const* name, Boolean const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Vector2f const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Vector3f const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }

        inline void Shader::SetConstant(Char const* name, rs::math::Vector4f const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }
        
        inline void Shader::SetConstant(Char const* name, rs::math::Matrix4x4f const* array, UInt32 const count) {
            SetRawConstant(name, reinterpret_cast< UInt8 const* >(array), sizeof(array[0]) * count);
        }

        inline Boolean Shader::HasGeometryShader() const {
            return hasGeometryShader_;
        }
        
        inline Boolean Shader::IsVertexShaderCompiled() const {
            return vertexShaderCompiled_;
        }
        
        inline Boolean Shader::IsFragmentShaderCompiled() const {
            return fragmentShaderCompiled_;
        }
        
        inline Boolean Shader::IsGeometryShaderCompile() const {
            return (HasGeometryShader() == false) || geometryShaderCompiled_;
        }
    }
}

#endif // RS_GRAPHICS_SHADEROBJECT_HPP